package cn.qn.framwork.security;

import cn.qn.dto.security.MemberDTO;
import cn.qn.enums.JwtAuthEnum;
import cn.qn.framwork.utils.OutUtils;
import cn.qn.moudle.security.MemberAccount;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * Created by sang on 2017/12/28.
 */
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Resource
    MemberAccountService memberAccountService;
    @Resource
    UrlFilterInvocationSecurityMetadataSource urlFilterInvocationSecurityMetadataSource;
    @Resource
    UrlAccessDecisionManager urlAccessDecisionManager;
    @Resource
    AuthenticationAccessDeniedHandler authenticationAccessDeniedHandler;
    @Resource
    private RedisTemplate<String,Object> redisTemplate;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(memberAccountService).passwordEncoder(new BCryptPasswordEncoder());
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/index.html", "/static/**","/login_p");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and().authorizeRequests()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O o) {
                        o.setSecurityMetadataSource(urlFilterInvocationSecurityMetadataSource);
                        o.setAccessDecisionManager(urlAccessDecisionManager);
                        return o;
                    }
                }).and()
                .addFilter(new JwtAuthFilter(authenticationManager()))
                .formLogin().loginPage("/login_p").loginProcessingUrl("/login").usernameParameter("username").passwordParameter("password").permitAll().failureHandler(new AuthenticationFailureHandler() {
            @Override
            public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
                httpServletResponse.setContentType("application/json;charset=utf-8");
                String userName = httpServletRequest.getParameter("username");
                String password = httpServletRequest.getParameter("password");
                StringBuffer sb = new StringBuffer();
                if(StringUtils.isBlank(userName)||StringUtils.isBlank(password)){
                    sb.append("请输入用户名和密码!");
                }else if(e instanceof UsernameNotFoundException || e instanceof BadCredentialsException) {
                    sb.append("用户名或密码输入错误，登录失败!");
                } else if (e instanceof DisabledException) {
                    sb.append("账户被禁用，登录失败，请联系管理员!");
                } else if(e instanceof LockedException){
                    sb.append("用户账号已锁定!");
                }else{
                    sb.append("登录失败!");
                }
                Integer time = (Integer)redisTemplate.opsForValue().get(userName);
                if(StringUtils.isNotBlank(userName)){
                    if(null == time){
                        time = 1;
                    }else{
                        time = time+1;
                    }
                    redisTemplate.opsForValue().set(userName,time,10*60,TimeUnit.SECONDS);
                }
                Map map = new HashMap<>();
                map.put("errorTime",time);
                OutUtils.print(OutUtils.ERROR_CODE,sb.toString(),map,httpServletResponse);
            }
        }).successHandler(new AuthenticationSuccessHandler() {
            @Override
            public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
                //生成JWT的token验证
                String token = Jwts.builder()
                        .setSubject(MemberAccountUtils.getCurrentMember().getUserId())
                        .setExpiration(new Date(System.currentTimeMillis() + JwtAuthEnum.EXPIRATION_TIME.getValue()))
                        .signWith(SignatureAlgorithm.HS512, JwtAuthEnum.SIGN_WITH.getCode())
                        .compact();

                MemberAccount memberAccount =MemberAccountUtils.getCurrentMember();
                redisTemplate.delete(memberAccount.getUserId());
                httpServletResponse.setContentType("application/json;charset=utf-8");
                MemberDTO vo = memberAccountService.createMemberDTO(memberAccount);
                vo.setAuthorization(JwtAuthEnum.AUTH_HEADER.getCode()+token);
                redisTemplate.opsForValue().set(token,vo, 24, TimeUnit.HOURS);
                OutUtils.print(OutUtils.SUCCESS_CODE,"登陆成功", vo,httpServletResponse);
            }
        }).and().logout().permitAll().and().csrf().disable().exceptionHandling().accessDeniedHandler(authenticationAccessDeniedHandler);
    }

    /**
     * 服务器跨域设置，允许不同环境跨域访问
     * @return
     */

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("http://localhost:8080","http://apptest.gaxndz.com","http://oms.evmylife.com","*"));
        configuration.setAllowedMethods(Arrays.asList("GET","POST"));
        configuration.setAllowedHeaders(Arrays.asList("*"));

        configuration.setAllowCredentials(true);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}